// 
//  Copyright © 2009 Jiří Zárevúcky <zarevucky.jiri@gmail.com>
// 
//  This program is free software: you can redistribute it and/or modify
//  it under the terms of the GNU Affero General Public License as
//  published by the Free Software Foundation, either version 3 of the
//  License, or (at your option) any later version.
// 
//  This program is distributed in the hope that it will be useful,
//  but WITHOUT ANY WARRANTY; without even the implied warranty of
//  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
//  GNU Affero General Public License for more details.
// 
//  You should have received a copy of the GNU Affero General Public License
//  along with this program.  If not, see <http://www.gnu.org/licenses/>.
// 
// 


using System;
using System.Collections.Generic;

using Galaxium.Protocol.Xmpp.Library.Xml;
using Galaxium.Protocol.Xmpp.Library.Utility;

namespace Galaxium.Protocol.Xmpp.Library.Core
{
	public class ResourceBinder
	{
		private const int BIND_TIMEOUT = 4000;
		private const int SESSION_TIMEOUT = 4000;
		
		private CoreStream   _stream;
		private List<string> _bound_resources;
		
		public ResourceBinder (CoreStream stream)
		{
			if (stream == null)
				throw new ArgumentNullException ();
			_stream = stream;
			_bound_resources = new List<string> ();
		}

		private Iq CreateBindQuery (bool unbind, string resource)
		{
			if (unbind && String.IsNullOrEmpty (resource))
				throw new ArgumentException ();

			Iq iq = new Iq (IqType.Set, unbind ? "unbind" : "bind", Namespaces.Bind);
			iq.Query.AppendChild (new Element ("resource").SetText (resource));
			return iq;
		}
		
		public JabberID BindResource (string resource)
		{
			if (_bound_resources.Contains (resource))
				throw new ArgumentException ("Resource already bound.");
			if (!_stream.Features.Supports ("bind", Namespaces.Bind))
				throw new NotSupportedException ();

			Iq response = _stream.SendQuery (CreateBindQuery (false, resource), BIND_TIMEOUT);
			var jid = new JabberID (response.FirstChild ("bind", null).GetTextChild ("jid", null));
			_bound_resources.Add (jid.Resource);

			if (_stream.Features.Supports ("session", Namespaces.Session) &&
			    !_stream.Features.IsOptional ("session", Namespaces.Session))
			    EngageSession ();
			    
			return jid;
		}

		public void UnbindResource (string resource)
		{
			if (resource == null)
				throw new ArgumentNullException ();
			if (!_stream.Features.Supports ("unbind", Namespaces.Bind))
				throw new NotSupportedException ();

			_stream.SendQuery (CreateBindQuery (true, resource), -1);
		}

		private void EngageSession ()
		{
			Iq iq = new Iq (IqType.Set, "session", Namespaces.Session);
			_stream.SendQuery (iq, SESSION_TIMEOUT);
		}
	}
}
